package win.larryzeal.spring.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.StandardPasswordEncoder;

import javax.sql.DataSource;

/**
 * 这是最简单的配置，拦截所有访问。
 * class body为空。
 * 必须配置一个bean 实现了WebSecurityConfigurer或者继承了WebSecurityConfigurerAdapter。
 * 任何实现了WebSecurityConfigurer的bean，在Spring中都是Spring Security的配置。--通常继承WebSecurityConfigurerAdapter更方便。
 *
 * @EnableWebSecurity已经自动添加了@EnableWebMvcSecurity。 Controller的方法参数可以使用@AuthenticationPrincipal来获取认证用户的principal（或用户名）。
 * <p>
 * Created by 张少昆 on 2017/8/26.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception{
//         http
//                 .formLogin()//支持基于form的认证（事先配置好一个login form页面）
//                 .and()
//                 .httpBasic()
//                 .and()
//                 .authorizeRequests().anyRequest().authenticated(); //所有请求都要被认证才可访问
        http
                .formLogin()
                .and()
                .authorizeRequests()
                .antMatchers("/spitter/me").hasRole("SPITTER")
                .antMatchers(HttpMethod.POST, "/spittles").hasRole("SPITTER")
                .anyRequest().permitAll()
                .and()
                .requiresChannel()
                .antMatchers("/spitter/form").requiresSecure();

//        http
//                .authorizeRequests()
//                .antMatchers("/spitters/me").authenticated()
//                .antMatchers(HttpMethod.POST, "/spittles").authenticated()
//                .antMatchers("/auth/**").authenticated() //
//                .regexMatchers("/auth/.*").authenticated()//和上面这句等同
//                //hasAnyAuthority(String…)
//                //hasAnyRole(String…)
//                //hasAuthority(String)
//                //hasIpAddress(String)
//                //hasRole(String) //其实就是hasAuthority("ROLE_xxx")
//                //rememberMe()
//                //not()
//                .anyRequest().permitAll();


        //使用方法来限定，缺点很明显，就是不能组合限定，如同时拥有角色和IP。这时就需要SpEL了，详见其支持的语句！
        //.access("hasRole('ROLE_SPITTER') and hasIpAddress('192.168.1.2')")

        // 如果涉及到需要加密的信息，如信用卡号之类的，最好创建一个channel，要求https请求！
        // .requiresChannel()
        //          .antMatchers("/spitter/form").requiresSecure();
        // 有些不需要加密，也可以这样：
        //.antMatchers("/").requiresInecure();
    }

    /**
     * 获取认证用户？
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        // auth
        //         .inMemoryAuthentication()//内存中  也可以关系型数据库等，或者自定义
        //         .withUser("user").password("password").roles("USER").and()
        //         .withUser("admin").password("password").roles("USER", "ADMIN");

        auth.jdbcAuthentication()//关系型数据库
                .dataSource(dataSource)//必须
                .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY) //重写查询语句
                .authoritiesByUsernameQuery(DEF_AUTHORITIES_BY_USERNAME_QUERY)//重写查询语句
                .groupAuthoritiesByUsername(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY)//重写查询语句
                .passwordEncoder(new StandardPasswordEncoder("53cr3t"));//密码的编码设置（不能明文） 需要一个PasswordEncoder实现
    }

    //说明，使用数据库时，有默认的查询语句，但通常不合适，需要覆盖掉。--下面是默认的！
    /***获取用户名、密码、是否启用*/
    public static final String DEF_USERS_BY_USERNAME_QUERY =
            "select username,password,enabled " +
                    "from users " +
                    "where username = ?";
    /***获取用户名、授权*/
    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
            "select username,authority " +
                    "from authorities " +
                    "where username = ?";
    /***获取授权用户组？*/
    public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
            "select g.id, g.group_name, ga.authority " +
                    "from groups g, group_members gm, group_authorities ga " +
                    "where gm.username = ? " +
                    "and g.id = ga.group_id " +
                    "and g.id = gm.group_id";
    //用户表、组表、权限表；组成员表、组权限表。
}
